package net.wanglu.www.zzz.rule.pronoun;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import net.wanglu.www.zzz.rule.BadEgg;
import net.wanglu.www.zzz.rule.Rule;
import net.wanglu.www.zzz.service.se.__SentenceElements;
import net.wanglu.www.zzz.service.se.nsubj._NSubjTuple;
import net.wanglu.www.zzz.service.se.obj._ObjectTuple;

import org.apache.commons.lang3.StringUtils;

import edu.stanford.nlp.dcoref.CorefChain;
import edu.stanford.nlp.dcoref.CorefChain.CorefMention;
import edu.stanford.nlp.dcoref.Dictionaries;
import edu.stanford.nlp.ling.TaggedWord;
import edu.stanford.nlp.trees.Tree;
/*
 * 代词与其指代的先行词在单复数/格/性别/人称都要保持一致
 */
public class PronounAV extends _PronounRule {

	public static final int AgreeInNumber = 1;
	public static final int AgreeInPerson = 2;
	
	public static final int SubjElement = 1;
	public static final int ObjElement = 2;
	
	public static final int Disagreement = 1;
	public static final int Vague = 2;
	
	public HashMap<Object, Object> desccribe = new HashMap<Object, Object>(16);
	{
		desccribe.put(Dictionaries.Animacy.ANIMATE, "有生命的事物");
		desccribe.put(Dictionaries.Animacy.INANIMATE, "没有生命的事物");
		desccribe.put(Dictionaries.Gender.MALE, "男性");
		desccribe.put(Dictionaries.Gender.FEMALE, "女性");
		desccribe.put(Dictionaries.Gender.NEUTRAL, "中性");
		desccribe.put(Dictionaries.Number.PLURAL, "复数形式");
		desccribe.put(Dictionaries.Number.SINGULAR, "单数形式");
		desccribe.put(_PronounRule.Person.First, "第一人称");
		desccribe.put(_PronounRule.Person.Senond, "第二人称");
		desccribe.put(_PronounRule.Person.Third, "第三人称");
	}
	
	@Override
	public Object applicable(Object... objects) throws Exception {
		// TODO Auto-generated method stub
		if ( super.applicable(objects) == Rule.Inapplicable) return Rule.Inapplicable;
		return Rule.Applicable;
	}

	@Override
	public Object deviseRightSolution(Object... objects) throws Exception {
		// TODO Auto-generated method stub
		sb.clear();
		sb.append("<p>");
		sb.append("代词的单复数由其先行项决定, 即同单同复");
		sb.append("</p>");
		sb.append("<p>");
		sb.append("代词的性别由其先行项的性别决定, 即同男同女");
		sb.append("</p>");
		sb.append("<p>");
		sb.append("代词的人称形式由其先行项的人称形式决定, 即同我同你同他");
		sb.append("</p>");
		sb.append("<p>");
		sb.append("代词的先行项是主语, 那么代词就用主格形式; 先行项是宾语, 代词就用宾格形式; 先行项是表所有的, 那么代词用所有格形式 .");
		sb.append("</p>");
		return sb.toString();
	}
	
	@Override
	public Object examine(Object... objects) throws Exception {
		if ( this.applicable(objects) == Rule.Inapplicable) return Rule.Inapplicable;
		HashMap<?, ?> preprocessData = (HashMap<?, ?>) objects[0];
		__SentenceElements se = (__SentenceElements) objects[1];
		
	    Map<?, ?> graph = (Map<?, ?>) preprocessData.get("coreChains");
	    
		Collection<?> values = graph.values();
		/*
		 * --Start--找到所有的Mention和所有代词性质的Mention
		 */
		HashMap<Integer, CorefMention> allMentions = new HashMap<Integer, CorefMention>(16);
		HashMap<Integer, CorefMention> allPronounMentions = new HashMap<Integer, CorefMention>(16);
		List<CorefMention> listMention = new ArrayList<CorefMention>(16);
		for ( Object obj : values) {
			CorefChain cc = (CorefChain) obj;
			List<CorefMention> textualOrderMentions = cc.getMentionsInTextualOrder();
			listMention.addAll(textualOrderMentions);
			for ( CorefMention cm : textualOrderMentions) {
				allMentions.put(cm.startIndex*100+cm.mentionSpan.length(), cm);
				if ( cm.mentionType == Dictionaries.MentionType.PRONOMINAL) {
					allPronounMentions.put(cm.startIndex*100+cm.mentionSpan.length(), cm);
				}
			}
		}
		TreeMap<Integer, CorefMention> sortedAllMentions = new TreeMap<Integer, CorefMention>(allMentions);
		TreeMap<Integer, CorefMention> sortedAllPronounMentions = new TreeMap<Integer, CorefMention>(allPronounMentions);
		/*
		 * --End--找到所有的Mention和所有代词性质的Mention
		 */
		/*
		 * ---Start---寻找Mention之间的包容关系，比如Tom and I这个metion就包容Tom和I这两个metion。
		 * 寻找这样的包容关系是为了防止在Tom和I之间进行代词指代的判定。
		 */
		HashMap<CorefMention, CorefMention> son_father = new HashMap<CorefMention, CorefMention>(16);
		for( int i=0; i<listMention.size(); i++) {
			/*
			 * 因为Mention肯定自己包含自己所以暂时先把father设定为自身。
			 */
			CorefMention son = listMention.get(i);
			CorefMention father = listMention.get(i);
			/*
			 * 再寻找还没有更大的包含
			 */
			for( int j=0; j<listMention.size(); j++) {
				if ( j == i) continue;
				CorefMention cm = listMention.get(j);
				if ( (" "+cm.mentionSpan+" ").contains(" "+son.mentionSpan+" ")) {//为了防止单词we被包容到单词week中，单词要加空格
					father = cm;
					break;
				}
			}
			son_father.put(son, father);
		}
		/*
		 * ---End---寻找Mention之间的包容关系，比如Tom and I这个metion就包容Tom和I这两个metion。
		 * 寻找这样的包容关系是为了防止在Tom和I之间进行代词指代的判定。
		 */
		/*
		 * --Start--为所有Mention确定它们是出现在主语成分中还是宾语成分中
		 */
		HashMap<CorefMention, Integer> elements = new HashMap<CorefMention, Integer>(16);
		for( _NSubjTuple nst : se.NSubjTupleList) {
			Tree subjAncestor = nst.getSubjectAncestor();
			ArrayList<TaggedWord> twl = subjAncestor.taggedYield();
			sb.clear();
			sb.append(" ");
			for( TaggedWord tw : twl) {
				sb.append(tw.word()).append(" ");
			}
			for( Map.Entry<Integer, CorefMention> entry : sortedAllMentions.entrySet()) {
				CorefMention cm = entry.getValue();
				if ( StringUtils.contains(sb.toString(), " "+cm.mentionSpan+" ")) {
					elements.put(cm, SubjElement);
				}
			}			
		}
		for( _ObjectTuple ot : se.ObjectTupleList) {
			Tree objectAncestor = ot.getObjectAncestor();
			ArrayList<TaggedWord> twl = objectAncestor.taggedYield();
			sb.clear();
			sb.append(" ");
			for( TaggedWord tw : twl) {
				sb.append(tw.word()).append(" ");
			}
			for( Map.Entry<Integer, CorefMention> entry : sortedAllMentions.entrySet()) {
				CorefMention cm = entry.getValue();
				if ( StringUtils.contains(sb.toString(), " "+cm.mentionSpan+" ")) {
					elements.put(cm, ObjElement);
				}
			}			
		}
		/*
		 * --End--为所有Mention确定它们是出现在主语成分中还是宾语成分中
		 */
		/*
		 * --Start--寻找代词错误
		 * 主要寻找两类错误，一类是代词无有所指表现为指代不一致，二类是代词多有所指表现为指代不明
		 */
		ArrayList<BadEgg> eggList = new ArrayList<BadEgg>(16);
		for( Map.Entry<Integer, CorefMention> entry : sortedAllPronounMentions.entrySet()) {
			Integer startIndex = entry.getKey();
			CorefMention pronounMention = entry.getValue();
			/*
			 * 如果当前的代词mention是在句子中是所有mention的第一个或者包含在第一个mention中，则不认为有指代问题，见例句12中的me
			 * 比如I love you. 代词I就虽然无有所指，但也不属于指代不一致的情况。
			 * Bob and I all love you. 这个代词I也不是无有所指，因此也不属于指代不一致的情况。
			 * 像这类代词就添加到BadEgg里了。
			 */
			boolean analyse = false;
			/*
			 * //有可能指代不一致也有可能根本就不想有所指。比如：
			 * In three weeks' time we will have to begin school again.
			 * 代词we就可以谁也不指。
			 * 需要设定一个标记，来区分究竟是指代不一致还是根本就不指。
			 */
			int flagScore = 10;
			/*
			 * --Start--寻找pronounMention究竟指代了几个先行词
			 * 如果是小于1个则表示有指代不一致的错误
			 * 如果是大于1个则表示有指代不明的错误
			 * 如果是等于1个则表示正确
			 */
			ArrayList<CorefMention> corefList = new ArrayList<CorefMention>(16);
			for ( Map.Entry<Integer, CorefMention> entry1 : sortedAllMentions.entrySet()) {
				Integer startIndex1 = entry1.getKey();
				if ( startIndex1 >= startIndex) break;//是因为已经排好序了，所以只要是大于等于出现了，后面就不用再比较了
				CorefMention cm = entry1.getValue();
				/*
				 * 如果当前mention包含代词mention或者当前metion和代词mention被同一个mention包含则不考虑指代关系。例如：
				 * metion1:Tom and I, metion2:Tom, metion3:I
				 * 如果metion1包含了metion3，则不考虑这两个metion之间是否有指代问题
				 * 如果metion2和metion3被相同的metion1包含，则也不考虑metion2和metion3之间是否有指代问题
				 */
				if ( cm.mentionSpan.contains(pronounMention.mentionSpan) || (son_father.get(pronounMention) == son_father.get(cm))) continue;
				analyse = true;
				int animacyScore = this.animacy(pronounMention, cm);
				int elementsScore = this.elements(pronounMention, cm, elements);
				int genderScore = this.gender(pronounMention, cm);
				int numberScore = this.number(pronounMention, cm);
				int personScore = this.person(pronounMention, cm);
				int score =  animacyScore + elementsScore + genderScore + numberScore + personScore;
				if ( animacyScore == 0 || elementsScore ==0 || genderScore == 0 || numberScore ==0 || personScore ==0) {
					if ( score > flagScore) {
						flagScore = score;
					}
					continue;
				}
				corefList.add(cm);
			}
			/*
			 * --End--寻找pronounMention究竟指代了几个先行词
			 */
			/*
			 * --Start--依据pronounMention究竟指代先行词的个数判断对错
			 * 如果是小于1个则表示可能有指代不一致的错误
			 * 如果是大于1个则表示有指代不明的错误
			 * 如果是等于1个则表示正确
			 */
			if ( corefList.size() < 1 && analyse && flagScore > 10) {
				if ( startIndex == sortedAllPronounMentions.firstKey() && sortedAllMentions.firstEntry().getValue().mentionSpan.contains(pronounMention.mentionSpan)) {
					//如果当前的代词mention是在句子中是所有mention的第一个或者包含在第一个mention中，则不认为有指代问题，见例句9中的our
					continue;
				}
				BadEgg egg = new BadEgg();
				egg.setViolatedRule(this);
				egg.setFlag(PronounAV.Disagreement);
				HashMap<String, Object> map = new HashMap<String, Object>(16);
				map.put("suspectPronounMention", pronounMention);
				map.put("elements", elements);
				map.put("candicateMentions", sortedAllMentions);
				egg.whereIsWrong = map;
				eggList.add(egg);
			} else if ( corefList.size() > 1) {//指代不明
				BadEgg egg = new BadEgg();
				egg.setViolatedRule(this);
				egg.setFlag(PronounAV.Vague);
				HashMap<String, Object> map = new HashMap<String, Object>(16);
				map.put("suspectPronounMention", pronounMention);
				map.put("elements", elements);
				map.put("candicateMentions", corefList);
				egg.whereIsWrong = map;
				eggList.add(egg);
			}

			/*
			 * --End--依据pronounMention究竟指代先行词的个数判断对错
			 */			
		}
		/*
		 * --End--寻找代词错误
		 */	
		
		if ( eggList.size() > 0) {
			return eggList;				
		}
		return null;
	}
	
	@Override
	public Object whyItsWrong(Object... objects) throws Exception {
		// TODO Auto-generated method stub
		BadEgg egg = (BadEgg) objects[0];
		
		int flag = egg.getFlag();
		HashMap<?, ?> map = (HashMap<?, ?>) egg.whereIsWrong; 
		CorefMention suspectPronounMention = (CorefMention) map.get("suspectPronounMention");
		HashMap<CorefMention, Integer> elements = (HashMap<CorefMention, Integer>) map.get("elements");
		
		if ( flag == PronounAV.Disagreement) {
			Integer tmpScore = 0;
			CorefMention cand = null;
			TreeMap<?, ?> candicateMentions = (TreeMap<?, ?>) map.get("candicateMentions");
			for( Map.Entry<?, ?> entry : candicateMentions.entrySet()) {
				Integer startIndex = (Integer) entry.getKey();
				if ( startIndex/100 >= suspectPronounMention.startIndex) break;
				CorefMention cm = (CorefMention) entry.getValue();
				if ( cm.mentionSpan.contains(" "+suspectPronounMention.mentionSpan+" ")) continue;
				Integer score = 0;
				score+=elements(suspectPronounMention, cm, elements);
				score+=animacy(cm,suspectPronounMention);
				score+=gender(cm,suspectPronounMention);
				score+=number(cm,suspectPronounMention);
				score+=person(cm,suspectPronounMention);
				
				if ( score > tmpScore) {
					tmpScore = score;
					cand = cm;
				}
			}
			
			sb.clear();
			if ( animacy(cand,suspectPronounMention) == 0) {
				sb.append("<p>");
				sb.append(cand.mentionSpan).append("是").append(desccribe.get(cand.animacy)).append(", 而");
				sb.append(suspectPronounMention.mentionSpan).append("是指代").append(desccribe.get(suspectPronounMention.animacy)).append("的.");
				sb.append("</p>");
			}
			
			if ( gender(cand,suspectPronounMention) == 0) {
				sb.append("<p>");
				sb.append(cand.mentionSpan).append("是").append(desccribe.get(cand.gender)).append(", 而");
				sb.append(suspectPronounMention.mentionSpan).append("是指代").append(desccribe.get(suspectPronounMention.gender)).append("的.");
				sb.append("</p>");
			}
			
			if ( number(cand,suspectPronounMention) == 0) {
				sb.append("<p>");
				sb.append(cand.mentionSpan).append("是").append(desccribe.get(cand.number)).append(", 而");
				sb.append(suspectPronounMention.mentionSpan).append("是指代").append(desccribe.get(suspectPronounMention.number)).append("的.");
				sb.append("</p>");
			}
			
			if ( person(cand,suspectPronounMention) == 0) {
				sb.append("<p>");
				sb.append(cand.mentionSpan).append("是").append(desccribe.get(this.whichPerson(cand))).append(", 而");
				sb.append(suspectPronounMention.mentionSpan).append("是指代").append(desccribe.get(this.whichPerson(suspectPronounMention))).append("的.");
				sb.append("</p>");
			}

			return sb.toString();
			
		} else if ( flag == PronounAV.Vague) {
			List<?> candicateMentions = (List<?>) map.get("candicateMentions");			
			sb.clear();
			sb.append("<p>");

			sb.append("代词");
			sb.append(suspectPronounMention.mentionSpan);
			sb.append("指代了:");
			for( Object obj : candicateMentions) {
				sb.append(obj).append(",");
			}
			sb.append("代词只能指涉一个名词或一个复合名词, 而且只能代替一个名词. 如果有两个或两个以上的名词之后出现一个代词, 就会出现指代不明的情况.");
			sb.append("</p>");
			return sb.toString();
		}
		return sb.toString();
	}


	private Integer elements(CorefMention pronoun, CorefMention cm, HashMap<CorefMention, Integer> element) {
		if ( element.get(cm) != null && _PronounRule.PronounsNominative.matcher(pronoun.mentionSpan).find() && element.get(cm) == PronounAV.SubjElement) {
			//都是主格
			return 10;
		} else if ( element.get(cm) != null && _PronounRule.PronounsObjective.matcher(pronoun.mentionSpan).find() && element.get(cm) == PronounAV.ObjElement) {
			//都是宾格
			return 10;
		} else if ( _PronounRule.PronounsPossessiveAdj.matcher(pronoun.mentionSpan).find()) {
			//pronoun是形容词所有格，另一方不是
			return 10;
		} else if ( cm.mentionType.equals(Dictionaries.MentionType.PRONOMINAL) && _PronounRule.PronounsPossessiveAdj.matcher(cm.mentionSpan).find()) {
			//cm是形容词所有格，另一方不是
			return 10;
		} else {
			return 0;
		}
	}
	private Integer animacy(CorefMention cm1, CorefMention cm2) {
		if ( cm1.animacy == cm2.animacy) {
			return 10;
		} else if ( cm1.animacy == Dictionaries.Animacy.UNKNOWN || cm2.animacy == Dictionaries.Animacy.UNKNOWN) {
			return 5;
		} else {
			return 0;
		}
	}
	
	private Integer gender(CorefMention cm1, CorefMention cm2) {
		if ( cm1.gender == cm2.gender) {
			return 10;
		} else if ( cm1.gender == Dictionaries.Gender.UNKNOWN || cm2.gender == Dictionaries.Gender.UNKNOWN) {
			return 5;
		} else {
			return 0;
		}
	}
	
	private Integer number(CorefMention cm1, CorefMention cm2) {
		/*
		 * --Start--如果CorefMention是由and构成的名词短语，本应为复数但stanford会判定为单数，因此要做复数处理
		 */
		Dictionaries.Number num1 = cm1.number;
		Dictionaries.Number num2 = cm2.number;
		if ( StringUtils.contains(cm1.mentionSpan, " and ")) {
			num1 = Dictionaries.Number.PLURAL;
		}
		if ( StringUtils.contains(cm2.mentionSpan, " and ")) {
			num2 = Dictionaries.Number.PLURAL;
		}
		/*
		 * --End--如果CorefMention是由and构成的名词短语，本应为复数但stanford会判定为单数，因此要做复数处理
		 */
		if ( num1 == num2) {
			return 10;
		} else if ( num1 == Dictionaries.Number.UNKNOWN || num2 == Dictionaries.Number.UNKNOWN) {
			return 5;
		} else {
			return 0;
		}
	}
	
	private Integer person(CorefMention cm1, CorefMention cm2) {

		int person1 = 3;
		if ( cm1.mentionType.equals(Dictionaries.MentionType.PRONOMINAL)) {
			if ( _PronounRule.FirstPerson.matcher(cm1.mentionSpan).find()) {
				person1 = 1;
			} else if ( _PronounRule.SecondPerson.matcher(cm1.mentionSpan).find()) {
				person1 = 2;
			}
		}
		
		int person2 = 3;
		if ( cm2.mentionType.equals(Dictionaries.MentionType.PRONOMINAL)) {
			if ( _PronounRule.FirstPerson.matcher(cm2.mentionSpan).find()) {
				person2 = 1;
			} else if ( _PronounRule.SecondPerson.matcher(cm2.mentionSpan).find()) {
				person2 = 2;
			}
		}
		
		if ( person1 == person2) return 10;
		
		return 0;
	}
	
	private Person whichPerson(CorefMention cm) {
		if ( _PronounRule.FirstPerson.matcher(cm.mentionSpan).find()) {
			return _PronounRule.Person.First;
		} else if ( _PronounRule.SecondPerson.matcher(cm.mentionSpan).find()) {
			return _PronounRule.Person.Senond;
		} else {
			return _PronounRule.Person.Third;
		}
	}
}
